/*
 * Copyright the original author or authors.
 * 
 * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.mozilla.org/MPL/MPL-1.1.html
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */	 
package com.kairos.transitions
{
	import flash.events.Event;
	
	import com.bourre.commands.ASyncCommand;
	import com.bourre.error.NullPointerException;
	import com.bourre.transitions.AbstractMultiTween;
	import com.bourre.transitions.Tween;
	import com.kairos.engine.RTAnimation;
	import com.kairos.engine.RTBeacon;
	import com.kairos.engine.RTEvent;
	import com.kairos.log.KairosDebug; 

	/**
	 * <code>MultiTweenRT</code> is a framerate independant motion tween that works
	 * according to all <code>RTBeacon</code> features. <code>MultiTweenRT</code>
	 * defines duration in milliseconds as all others <code>RTObject</code>.
	 * <p>
	 * <code>MultiTweenRT</code> extends <code>AbstractMultiTween</code> from
	 * LowRA and add support for time global and local scaling according to the
	 * <code>RTAnimation</code> interface.
	 * </p><p>
	 * Tweens support properties and function as target field for the animation.
	 * When specifying a function as target you also have to define the <code>getter</code>
	 * parameter to offer an access at the object to get start value from the target object.
	 * </p><p>
	 * By default, a tween don't need a start value, it take the current target
	 * property value as start value, if the target property is a function you have
	 * to define the getter parameter when creating the tween.
	 * </p><p>
	 * You can use easing functions in order to modify the values interpolations
	 * as with any others <code>Tween</code> objects.
	 * </p> 
	 * @author 	Cédric Néhémie
	 * @example Creating a tween on a many properties of a sprite.
	 * 
	 * <listing>import com.kairos.transtions.MultiTweenRT;
	 * import flash.display.Sprite;
	 * 
	 * var sprite : Sprite = new Sprite();
	 * var tween : MultiTweenRT = new MultiTweenRT( sprite, 
	 * 												[ "x", "y", "alpha" ], 
	 * 												[ 500, 400, 0 ] , 
	 * 												1000 );
	 * tween.start();</listing>
	 * 
	 * Creating a tween on a sole property of many objects.
	 * 
	 * <listing>import com.kairos.transtions.MultiTweenRT;
	 * import flash.display.Sprite;
	 * 
	 * var sprite1 : Sprite = new Sprite();
	 * var sprite2 : Sprite = new Sprite();
	 * var sprite3 : Sprite = new Sprite();
	 * 
	 * var tween : MultiTweenRT = new MultiTweenRT( [ sprite1, sprite2, sprite3 ],
	 * 												[ "x", "x", "x" ],
	 * 												[ 500, 500, 500 ],
	 * 												1000 );
	 * tween.start();</listing>
	 * 
	 * Creating a tween on a mix of properties and methods of an object.
	 * In that example the start and easing are set on  <code>null</code>
	 * to keep the default behavior. The first entry of the getters array
	 * is set to null because a property is a setter and a getter at the
	 * same time.
	 * 
	 * <listing>import com.kairos.transtions.TweenRT;
	 * 
	 * var target : MyCustomClass = new MyCustomClass();
	 * var tween : MultiTweenRT = new MultiTweenRT( target,
	 * 												[ "property", "setProperty" ],
	 * 												[ 500, 100 ],
	 * 												1000,
	 * 												null,
	 * 												null,
	 * 												[ null, "getProperty" ] );
	 * tween.start();</listing>
	 */
	public class MultiTweenRT extends AbstractMultiTween
	{
		/**
		 * Local speed factor for the object
		 */	
		protected var _nLocalSpeed : Number;
		
		/**
		 * Create a new tween for the passed-in target object(s).
		 * <p>
		 * By default, a tween don't need a start values, it take the current target
		 * properties values as start values, if the target(s) property are functions you have
		 * to define the <code>getters</code> parameter when calling the constructor in
		 * order to provide to the tween an access to the start values.
		 * </p><p>
		 * If the easing function is ommitted the <code>noEasing</code> method
		 * of the <code>AbstractTween</code> class.
		 * </p>
		 * 
		 * @param 	target		<code>Object</code> or <code>Array</code> of objects onto with
		 * 						the tween will runs.
		 * @param 	setters		targets properties or methods on the objects, if a collection
		 * 						of objects is passed each entry is associated to the entry at
		 * 						the same place in <code>target</code>.
		 * @param 	ends		end values for each tweens.
		 * @param 	duration  	<code>Number</code> of milliseconds of the tween's duration.
		 * @param 	starts		start values for each tweens.
		 * @param 	easing		an easing function to run on each tweens.
		 * @param 	getters		an array of properties names for getting values from setter functions
		 * 						properties.
		 */
		public function MultiTweenRT( target : Object, setters : Array, ends : Array, duration : Number, starts : Array = null, easing : Function = null, getters : Array = null )
		{
			super( target, setters, ends, duration, starts, easing, getters );
			_oBeacon = RTBeacon.getInstance();
			_nLocalSpeed = 1;
		}
		
		/**
		 * Proceed to the animation of the tween.
		 * <p>
		 * That method have been override to support the real time processing
		 * implementation of Kairos.
		 * </p>
		 * @param e <code>Event</code> of <code>TickListener</code> implementation.
		 */
		override public function onTick( e : Event = null ) : void
		{
			_nPlayHead += ( e as RTEvent ).getStep() * _nLocalSpeed * ( _bReversedMotion ? -1 : 1 );
			super.onTick( e );
		}
		
		/**
		 * Returns <code>true</code> if the animation finish after
		 * the current move.
		 * 
		 * @return 	<code>true</code> if the animation finish after
		 * 			the current move.
		 */
		override public function isMotionFinished() : Boolean
		{
			return _nPlayHead >= _nDuration;
		}
		/**
		 * Returns <code>true</code> if the animation finish after
		 * the current move.
		 * 
		 * @return 	<code>true</code> if the animation finish after
		 * 			the current move.
		 */
		override public function isReversedMotionFinished() : Boolean
		{
			return _nPlayHead <= 0;
		}
		
		/**
		 * Defines a new <code>TickBeacon</code> object the object will run on.
		 * <p>
		 * Use that method carefully, if you have objects in your scene which 
		 * are plugged on two beacon which have differents settings you can loose
		 * synchronisation between those objects.
		 * </p><p>
		 * If the passed-in beacon is null, the function failed and trace an error
		 * on the Kairos log channel.
		 * </p>
		 * 
		 * @param 	b	<code>TickBeacon</code> to register with.
		 * @throws 	<code>NullPointerException</code> — The passed-in beacon is null.
		 */
		public function setTickBeacon( b : RTBeacon ) : void
		{
			if( !b ) 
			{
				KairosDebug.WARN( "Trying to set a null beacon as beacon for " + this );
				throw new NullPointerException ( "Trying to set a null beacon as beacon for " + this );
			}
			
			if( _bIsRunning )
			{ 
				stop();
				_oBeacon = b;
				start();
			}
			else
			{
				_oBeacon = b;
			}
		}
		
		/**
		 * Returns the <code>RTBeacon</code> used by the current object.
		 * 
		 * @return <code>RTBeacon</code> used by the current object.
		 */
		public function getTickBeacon () : RTBeacon
		{
			return _oBeacon as RTBeacon;
		}	
		
		/**
		 * Defines the local speed factor for the current object. Local speed works
		 * as a multiplier for the time step value received from the beacon - as the
		 * <code>setGlobalSpeed</code> feature of the RTBeacon.
		 * <p>
		 * The default <code>setLocalSpeed</code> method don't support value lower
		 * or equals than 0, override it if you want to avoid negative values.
		 * </p>
		 * @param 	rate float number used as multiplier for the current time step
		 * @throws 	<code>RangeError</code> — The value specified in <code>setLocalSpeed</code>
		 * 			isn't supported by the current object.	
		 */
		public function setLocalSpeed ( rate : Number ) : void
		{
			if( rate <= 0 )
			{
				KairosDebug.ERROR( rate + " can't be lower or equals than 0 in " + this + ".setLocalSpeed()" );
				throw new RangeError (" The value specified in setLocalSpeed isn't supported by the current object." );	
			}
			_nLocalSpeed = rate;
		}
		
		/**
		 * Returns the current local speed factor of the object.
		 * 
		 * @return <code>Number</code> local speed of the object.
		 */
		public function getLocalSpeed () : Number
		{
			return _nLocalSpeed;
		}
	}
}